#ifndef _DMA_HW_H_
#define _DMA_HW_H_

#include "soc.h"

/**
 * 单次传输：
 *     1. DMA每传输一个项目，就申请一次总线仲裁;
 *
 * 突发传输：
 *     1. DMA完成对应的节拍数，才会放弃总线，传输期间一直占用总线;
 *     2. 该模式必须使用FIFO，突发传输的节拍数和项目大小以及FIFO深度有关;
 *     3. 根据AHB协议，一次突发传输不能跨1KB地址边界;
 *
 * 直接传输：
 *     1. DMA有FIFO，可以将源数据先保存到FIFO再传输到目的地址，直接传输就
 *        是禁用FIFO，该模式只能使用单次传输;
 *     2. 直接传输源和目的位宽必须相等，统一由PSIZE指定，MSIZE将被忽略;
 *     3. 不支持突发传输;
 *     4. 不持支存储到存储的传输;
 *
 * 循环传输：
 *     1. DMA传输完毕后会加载上一次的配置值，主要是"项目数"等下一次开
 *        始时可以直接传输，不用再配置;
 *     2. 可以实现环形buf的数据搬运，配置时NDTR=环形buf的大小，当NDTR减
 *        到0时，会重新设置NDTR=环形buf的大小，从buf的首地址开始传输数据;
 * 
 * 暂停传输：
 *     1. 设置CR_EN=0，此时DMA在完成当前正在传输的事务后（单次传输或单次突
 *        发传输的）停止，之后需要软件根据NDTR中的剩余字节数计算出断点续传
 *        的地址，之后再重新使能;
 *
 * 流传输：
 *     1. 由外设控制DMA的结束，而不是NDTR，需要相应的外设具有通知DMA结束的
 *        功能;
 *     2. 不支持循环传输，不支持存储到存储的传输;
 *     3. 使能传输后，NDTR会被硬件设置为0xFFFF，传输过程中会进行递减，减到0
 *        会停止传输，也即单次最大可传输65535个数据项。也可根据NDTR中的值
 *        计算出实际搬运了多少项;
 *
 * 封包解包：
 *     1. 即源和目的的位宽不一致，需要使用FIFO进行封包和解包;
 *
 * 可能的DMA配置：
 *     ---------------------------------------------------------
 *     | 传输 | 源 | 目标 | 流控 | 循环 | 类型 | 直接 | 双缓冲 |
 *     |-------------------------------------------------------|
 *     | P2M  | P  |  M   | DMA  | 允许 | 单独 | 允许 |  允许  |
 *     | P2M  | P  |  M   | DMA  | 允许 | 突发 | 禁止 |  允许  |
 *     | P2M  | P  |  M   | FLOW | 禁止 | 单次 | 允许 |  禁止  |
 *     | P2M  | P  |  M   | FLOW | 禁止 | 突发 | 禁止 |  禁止  |
 *     |-------------------------------------------------------|
 *     | M2P  | M  |  P   | DMA  | 允许 | 单独 | 允许 |  允许  |
 *     | M2P  | M  |  P   | DMA  | 允许 | 突发 | 禁止 |  允许  |
 *     | M2P  | M  |  P   | FLOW | 禁止 | 单次 | 允许 |  禁止  |
 *     | M2P  | M  |  P   | FLOW | 禁止 | 突发 | 禁止 |  禁止  |
 *     |-------------------------------------------------------|
 *     | M2M  | P  |  M   | DMA  | 禁止 | 单独 | 禁止 |  禁止  |
 *     | M2M  | P  |  M   | DMA  | 禁止 | 突发 | 禁止 |  禁止  |
 *     |--------------------------------------------------------
 */

typedef struct {
	_RW CR;     /*!< DMA stream x configuration register      */
	_RW NDTR;   /*!< DMA stream x number of data register     */
	_RW PAR;    /*!< DMA stream x peripheral address register */
	_RW M0AR;   /*!< DMA stream x memory 0 address register   */
	_RW M1AR;   /*!< DMA stream x memory 1 address register   */
	_RW FCR;    /*!< DMA stream x FIFO control register       */
} DMA_StreamStruct;

typedef struct {
	_OR ISR[2];   /*!< DMA low interrupt status register,      offset: 0x00-0x04 */
	_RW IFCR[2];  /*!< DMA low interrupt flag clear register,  offset: 0x08-0x0C */
} DMA_Struct;

extern DMA_Struct SOC_DMA1;
extern DMA_Struct SOC_DMA2;
extern DMA_StreamStruct SOC_DMA1_S0;
extern DMA_StreamStruct SOC_DMA1_S1;
extern DMA_StreamStruct SOC_DMA1_S2;
extern DMA_StreamStruct SOC_DMA1_S3;
extern DMA_StreamStruct SOC_DMA1_S4;
extern DMA_StreamStruct SOC_DMA1_S5;
extern DMA_StreamStruct SOC_DMA1_S6;
extern DMA_StreamStruct SOC_DMA1_S7;
extern DMA_StreamStruct SOC_DMA2_S0;
extern DMA_StreamStruct SOC_DMA2_S1;
extern DMA_StreamStruct SOC_DMA2_S2;
extern DMA_StreamStruct SOC_DMA2_S3;
extern DMA_StreamStruct SOC_DMA2_S4;
extern DMA_StreamStruct SOC_DMA2_S5;
extern DMA_StreamStruct SOC_DMA2_S6;
extern DMA_StreamStruct SOC_DMA2_S7;

/**
 * 中断标志，ISR查询，IFCR写1清除
 */
#define DMA_IDX_0_3D(s)         0x0
#define DMA_IDX_4_7D(s)         0x1
#define DMA_FLAG_FEIF           0x01U  /* FIFO错误 */
#define DMA_FLAG_DMEIF          0x04U  /* 直接模式错误 */
#define DMA_FLAG_TEIF           0x08U  /* 传输错误 */
#define DMA_FLAG_HTIF           0x10U  /* 半传输中断 */
#define DMA_FLAG_TCIF           0x20U  /* 传输完成中断 */
#define DMA_FLAG_ALL            0x3DU  /* 所有的标志 */
#define DMA_S0_FLAG(flag)       ((flag)<<0)
#define DMA_S1_FLAG(flag)       ((flag)<<6)
#define DMA_S2_FLAG(flag)       ((flag)<<16)
#define DMA_S3_FLAG(flag)       ((flag)<<22)
#define DMA_S4_FLAG(flag)       ((flag)<<0)
#define DMA_S5_FLAG(flag)       ((flag)<<6)
#define DMA_S6_FLAG(flag)       ((flag)<<16)
#define DMA_S7_FLAG(flag)       ((flag)<<22)

/**
 * DMA数据流配置
 */
#define _DMA_IsBusy(dev)        ((dev).CR & DMA_CR_EN)
#define DMA_CR_DIS              (0x0U<<0)   /* 停止传输 */
#define DMA_CR_EN               (0x1U<<0)   /* 使能传输，传输出错或传输完成，
                                               由硬件清零，该位为1时禁止修改
                                               其他寄存器，启动前需要先清除
                                               ISR中的标志 */
#define DMA_CR_DMEIE_DIS        (0x0U<<1)
#define DMA_CR_DMEIE_EN         (0x1U<<1)   /* 使能直接模式错误中断 */
#define DMA_CR_TEIE_DIS         (0x0U<<2)
#define DMA_CR_TEIE_EN          (0x1U<<2)   /* 使能传输错误中断 */
#define DMA_CR_HTIE_DIS         (0x0U<<3)
#define DMA_CR_HTIE_EN          (0x1U<<3)   /* 使能半完成中断 */
#define DMA_CR_TCIE_DIS         (0x0U<<4)
#define DMA_CR_TCIE_EN          (0x1U<<4)   /* 使能传输完成中断 */
#define DMA_CR_PFCTRL_DMA       (0x0U<<5)   /* 由DMA控制传输完成，存储到存储时
                                               强制为0 */
#define DMA_CR_PFCTRL_FLOW      (0x1U<<5)   /* 由外设控制传输完成，不定长数
                                               据 */
#define DMA_CR_DIR_P2M          (0x0U<<6)   /* 外设到存储 */
#define DMA_CR_DIR_M2P          (0x1U<<6)   /* 存储到外设 */
#define DMA_CR_DIR_M2M          (0x2U<<6)   /* 存储到存储，不支持双缓冲机制，
											   只有DMA2支持存储到存储 */
#define DMA_CR_CIRC_DIS         (0x0U<<8)   /* 禁止循环模式，外设控制传输完成
                                               时，强制为0 */
#define DMA_CR_CIRC_EN          (0x1U<<8)   /* 使能循环模式，使用双缓冲时，
                                               强制为1 */
#define DMA_CR_PINC_DIS         (0x0U<<9)   /* 外设地址固定 */
#define DMA_CR_PINC_EN          (0x1U<<9)   /* 外设地址递增 */
#define DMA_CR_MINC_DIS         (0x0U<<10)  /* 存储地址固定 */
#define DMA_CR_MINC_EN          (0x1U<<10)  /* 存储地址递增 */
#define DMA_CR_PSIZE_1          (0x0U<<11)  /* 外设地址加1，PINC_DIS时无关 */
#define DMA_CR_PSIZE_2          (0x1U<<11)  /* 外设地址加2 */
#define DMA_CR_PSIZE_4          (0x2U<<11)  /* 外设地址加4 */
#define DMA_CR_MSIZE_1          (0x0U<<13)  /* 存储器地址加1 */
#define DMA_CR_MSIZE_2          (0x1U<<13)  /* 存储器地址加2 */
#define DMA_CR_MSIZE_4          (0x2U<<13)  /* 存储器地址加4 */
#define DMA_CR_PINCOS_DIS       (0x0U<<15)  /* 外设递增由PSIZE决定，PINC=0时
                                               该位被忽略，直接模式或PBURST
                                               !=0，则强制为0 */
#define DMA_CR_PINCOS_EN        (0x1U<<15)  /* 外设递增固定为4，PINC=0时该位
                                               被忽略 */
#define DMA_CR_PL_LOW           (0x0U<<16)  /* 最低优先级 */
#define DMA_CR_PL_0             (0x0U<<16)  /* 最低优先级 */
#define DMA_CR_PL_1             (0x1U<<16)  /* 中优先级 */
#define DMA_CR_PL_2             (0x2U<<16)  /* 高优先级 */
#define DMA_CR_PL_3             (0x3U<<16)  /* 最高优先级 */
#define DMA_CR_PL_HIGH          (0x3U<<16)  /* 最高优先级 */
#define DMA_CR_DBM_DIS          (0x0U<<18)
#define DMA_CR_DBM_EN           (0x1U<<18)  /* 使能双缓冲区模式，CIRC被强制
                                               为1 */
#define DMA_CR_CT_M0            (0x0U<<19)  /* 当前使用M0AR寻址，EN使能前可修
                                               改，之后仅用作状态标志 */
#define DMA_CR_CT_M1            (0x1U<<19)  /* 当前使用M1AR寻址，EN使能前可修
                                               改，之后仅用作状态标志 */
#define DMA_CR_PBURST_SINGLE    (0x0U<<21)  /* 单次传输，直接模式下强制为0 */
#define DMA_CR_PBURST_INCR4     (0x1U<<21)  /* 突发传输4节拍 */
#define DMA_CR_PBURST_INCR8     (0x2U<<21)  /* 突发传输8节拍 */
#define DMA_CR_PBURST_INCR16    (0x3U<<21)  /* 突发传输16节拍 */
#define DMA_CR_MBURST_SINGLE    (0x0U<<23)  /* 单次传输，直接模式下强制为0 */
#define DMA_CR_MBURST_INCR4     (0x1U<<23)  /* 突发传输4节拍 */
#define DMA_CR_MBURST_INCR8     (0x2U<<23)  /* 突发传输8节拍 */
#define DMA_CR_MBURST_INCR16    (0x3U<<23)  /* 突发传输16节拍 */
#define DMA_CR_CHSEL_0          (0x0U<<25)  /* 选择通道0，存储到存储时通道无关 */
#define DMA_CR_CHSEL_1          (0x1U<<25)  /* 选择通道1 */
#define DMA_CR_CHSEL_2          (0x2U<<25)  /* 选择通道2 */
#define DMA_CR_CHSEL_3          (0x3U<<25)  /* 选择通道3 */
#define DMA_CR_CHSEL_4          (0x4U<<25)  /* 选择通道4 */
#define DMA_CR_CHSEL_5          (0x5U<<25)  /* 选择通道5 */
#define DMA_CR_CHSEL_6          (0x6U<<25)  /* 选择通道6 */
#define DMA_CR_CHSEL_7          (0x7U<<25)  /* 选择通道7 */
#define _DMA_GetChannel(dev)    (((dev).CR >> 25) & 0x7)

/**
 * 数据项，EN使能前可配置，之后仅用作指示剩余待传输的项数，传输完成后寄存器
 * 保持0，在循环模式下会重新装载之前的值，重新使能也会装载之前的值。该值是
 * 依据外设端口的宽度来统计的，源和目的位宽不同时，应保持好项目数的比例，以
 * 确保数据传输的完整性
 */
#define DMA_NDTR_0_65535D(val)  (val)

/**
 * 外设地址，外设到存储器、存储器到外设时用于保存外设的地址，存储器到存储器
 * 时，用于保存源地址，使能传输期间，禁止修改
 */
#define DMA_PAR_0_FFFFFFFFH(addr) ((uint32_t)(addr))

/**
 * 存储器0地址，外设到存储器、存储器到外设时用于保存存储器的地址，存储器到
 * 存储器时，用于保存目的地址。双缓存模式下使能传输后当CT=1时可修改
 */
#define DMA_M0AR_0_FFFFFFFFH(addr) ((uint32_t)(addr))

/**
 * 存储器1地址，外设到存储器、存储器到外设时用于保存存储器的地址，存储器到
 * 存储器时，用于保存目的地址。仅在双缓存模式下有效，使能传输后当CT=0时可修改
 */
#define DMA_M1AR_0_FFFFFFFFH(addr) ((uint32_t)(addr))

/**
 * FIFO配置
 */
#define DMA_FCR_FTH_1              (0x0U<<0)  /* 1/4容量，1个字，直接模式下
                                                 (DMDIS=0)不使用FIFO */
#define DMA_FCR_FTH_2              (0x1U<<0)  /* 2/4容量，2个字 */
#define DMA_FCR_FTH_3              (0x2U<<0)  /* 3/4容量，3个字 */
#define DMA_FCR_FTH_4              (0x3U<<0)  /* 4/4容量，4个字 */
#define DMA_FCR_DMDIS_DIS          (0x1U<<2)  /* 存储器到存储器的传输，该位
                                                 强制为1 */
#define DMA_FCR_DMDIS_EN           (0x0U<<2)  /* 使能直接模式 */
#define DMA_FCR_FS_LEVEL1          (0x0U<<3)  /* 0 <  FIFO < 1，只读状态，
                                                 直接模式下(DMDIS=0)不使用
                                                 FIFO */
#define DMA_FCR_FS_LEVEL2          (0x1U<<3)  /* 1 <= FIFO < 2 */
#define DMA_FCR_FS_LEVEL3          (0x2U<<3)  /* 2 <= FIFO < 3 */
#define DMA_FCR_FS_LEVEL4          (0x3U<<3)  /* 3 <= FIFO < 4 */
#define DMA_FCR_FS_EMPTY           (0x4U<<3)  /* FIFO = 0 */
#define DMA_FCR_FS_FULL            (0x5U<<3)  /* FIFO = 4 */
#define DMA_FCR_FEIE_DIS           (0x0U<<7)
#define DMA_FCR_FEIE_EN            (0x1U<<7)  /* 使能FIFO错误中断 */

#endif /* _DMA_HW_H_ */
